﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Clustering_IA_Project
{
    /// <summary>
    /// Metodo k-means
    /// </summary>
    class Kmeans
    {

        int passo, numGruppi;
        int coeffMin;
        bool fine, errore;
        SimilarityMatrix dati;
        List<Item>[] centroidi;
        float[] valCentroide;

        /// <summary>
        /// Costruttore
        /// </summary>
        /// <param name="dati">SimilarityMatrix la matrice delle similarita'</param>
        /// <param name="numGruppi">numero di gruppi da formare</param>
        /// <param name="coeffMin">coeff di Minkowski</param>
        public Kmeans(SimilarityMatrix dati, int numGruppi, int coeffMin)
        {
            this.numGruppi = numGruppi;
            this.coeffMin = coeffMin;
            passo = 1;
            fine = false;
            errore = false;
            this.dati = dati;
            centroidi = new List<Item>[numGruppi];
            for (int i = 0; i < numGruppi; i++)
            {
                centroidi[i] = new List<Item>();
            }
            valCentroide = new float[numGruppi];
        }

        /// <summary>
        /// Avvia il metodo delle K medie       
        /// </summary>
        public void kmeans()
        {
            while (true)
            {
                if (errore == true) break;
                if (passo == 1) this.passo1();   //crea i gruppi di partenza 
                //else if (passo == 2) this.passo2();     //calcola i centroidi
                //else if (passo == 3) this.passo3();     //riassegna i gruppi
                //else if ((passo == 4) && (fine==false)) this.passo2();   
                else if ((passo == 4) && (fine == true)) break;      //se stabile si ferma
                else if ((passo == 4) && (errore == true)) break;      //se stabile si ferma
                break;
            }
        }

        /// <summary>
        /// Calcola la distanza di Minkowski
        /// </summary>
        /// <param name="uno"></param>
        /// <param name="due"></param>
        /// <returns></returns>
        private float calcolaDist(float uno, float due)
        {
            float k = 0f, t = 0f;
            //for(int w=0; w<numVar; w++){
            //k = Math.abs(uno[w] - due[w]);
            //k= Math.pow(k, coeffMin);
            //t=t+k;
            //}
            //return (double)Math.pow(t,(1.0/coeffMin)); 
            k = Math.Abs(uno - due);
            return k;
        }

        /// <summary>
        /// Assegno gli elementi ai gruppi all'inizio a caso
        /// </summary>
        private void passo1()
        {
            float dist1 = 0f, dist2 = 0f;
            if (dati.getMatrix().Count <= numGruppi)
            {
                //("Parametri errati, diminuire il numero di gruppi!");
                errore = true;
                passo = 4;
                return;
            }
            //lunghezza dei gruppi dalla dalla divisione intera numElementi / numGruppi
            int lenGeneral = dati.getMatrix().Count / numGruppi;
            //al primo gruppo aggiungo il resto della divisione intera di prima
            int firstGroup = (dati.getMatrix().Count % numGruppi) + lenGeneral;
            //formo il primo gruppo
            for (int i = 0; i < firstGroup; i++)
            {
                centroidi[0].Add(dati.getMatrix()[i]);
            }
            //formo gli alri gruppi
            //per il numero di gruppi rimanente cioe' numGruppi meno 1(il primo gia fatto)
            for (int k = 0; k < numGruppi - 1; k++)
            {
                //formo ogni singolo gruppo
                for (int j = 0; j < lenGeneral; j++)
                {
                    centroidi[k].Add(dati.getMatrix()[j]);
                }
            }
            passo = 2;
        }

        /// <summary>
        /// Calcolo la media dei centrtidi
        /// </summary>
        /*
        public void passo2()
        {   
            float k;
            foreach(Item it in centroidi)
            {


            for(int i=0; i<numGruppi; i++)
            {
                for(int j=0; j<numVar; j++){
                    k=0.0;
                    for (Iterator s = gruppi.get(i).iterator(); s.hasNext();){
                        int[] dato = (int[]) s.next();  
                        k = k + dato[j];
                    }
                    k = k/((double)(gruppi.get(i).size()));
                    daCaricare[j]=k;
                }
                centroidi.set(i, daCaricare);
            }
            passo=3;
        }*/

        /*
        private double calcolaDistCentr(int[] uno, double[] due)
        {

            double k = 0.0, t = 0.0;
            for (int w = 0; w < numVar; w++)
            {

                k = Math.abs(((double)uno[w]) - due[w]);
                k = Math.pow(k, coeffMin);
                t = t + k;
            }
            return (double)Math.pow(t, (1.0 / coeffMin));  //metti 1.0 e non 1 per forzare il risultato a reale e no a intero
        }*/

        /*
        public void passo3()
        {

            double dist1, dist2;
            int[] app;
            HashMap<Integer, int[]> daTogliere = new HashMap<Integer, int[]>();
            HashMap<Integer, int[]> daMettere = new HashMap<Integer, int[]>();
            int cont, spostamenti = 0;

            for (int i = 0; i < numGruppi; i++)
            {

                Iterator itr = gruppi.get(i).iterator();

                while (itr.hasNext())
                {
                    app = (int[])itr.next();
                    cont = 0;
                    dist1 = calcolaDistCentr(app, centroidi.get(cont));

                    for (int j = 1; j < numGruppi; j++)
                    {
                        dist2 = calcolaDistCentr(app, centroidi.get(j));
                        if (dist1 >= dist2)
                        {
                            dist1 = dist2;
                            cont = j;
                        }
                    }
                    if (cont != i)
                    {
                        daTogliere.put(i, app);
                        daMettere.put(cont, app);
                    }
                }
            }
            Integer key;
            Iterator mit = daTogliere.keySet().iterator();
            while (mit.hasNext())
            {
                key = (Integer)mit.next();
                app = (int[])daTogliere.get(key);
                gruppi.get(key).remove(app);
            }
            Iterator mit2 = daMettere.keySet().iterator();
            while (mit2.hasNext())
            {
                key = (Integer)mit2.next();
                app = (int[])daMettere.get(key);
                gruppi.get(key).add(app);
                spostamenti++;
            }
            if (spostamenti == 0) fine = true;
            passo = 4;
        }*/

        /*
         * Stampa gli elementi di ogni gruppo ottenuto  nella console. <br/>
         * Utile in fase di debug
         
        public void stampa(){
        
            int[] app;
            for(int i=0; i<numGruppi; i++){
                System.out.println("Nel gruppo: " + i+ "sono presenti");
                Iterator it= gruppi.get(i).iterator();
                while(it.hasNext()){
            
                    app= (int[]) it.next();
                    System.out.print("[");
                
                    if(app==null) continue;
                
                    for(int w=0; w<app.length ;w++){
                        System.out.print(app[w]+ ",");
                    }
                    System.out.println("]");
                }
            }
            System.out.println("fine stampa");
        }
        */
    }
}
